package com.naiterui.ehp.bs.doctor.service.impl;

import java.net.URLEncoder;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.common.repository.page.CustomizedPageConvertor;
import com.naiterui.common.repository.page.Page;
import com.naiterui.common.repository.page.PageParamVO;
import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.bo.doctor.PatientRealtionStatusBO;
import com.naiterui.ehp.bp.bo.doctor.RecomPatientBO;
import com.naiterui.ehp.bp.bo.doctor.RecomPatientBO.AgeUnitEunm;
import com.naiterui.ehp.bp.bo.emr.MedicalRecordScreenBO;
import com.naiterui.ehp.bp.bo.patient.FamilyBO;
import com.naiterui.ehp.bp.bo.patient.PatientInquirerBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.constants.DoctorPatientRelation;
import com.naiterui.ehp.bp.domain.*;
import com.naiterui.ehp.bp.support.exception.BaseExceptionCodes;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.exception.ResponseVO;
import com.naiterui.ehp.bp.utils.biz.NameUtils;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.encypt.DESPlus;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bp.utils.lang.FirstLetterUtil;
import com.naiterui.ehp.bp.utils.lang.StringUtil;
import com.naiterui.ehp.bp.utils.net.RestHttpUtil;
import com.naiterui.ehp.bp.utils.sort.ABCUtil.ABCVO;
import com.naiterui.ehp.bs.doctor.dao.*;
import com.naiterui.ehp.bs.doctor.dao.mongo.AdImSetupDao;
import com.naiterui.ehp.bs.doctor.dao.mongo.AdPatientDocumentDao;
import com.naiterui.ehp.bs.doctor.domain.PatientpersonalDoctorLog;
import com.naiterui.ehp.bs.doctor.enums.PatientPersonalTips;
import com.naiterui.ehp.bs.doctor.exception.ExceptionCodes;
import com.naiterui.ehp.bs.doctor.fegin.EmrFeignClient;
import com.naiterui.ehp.bs.doctor.fegin.PatientFeignClient;
import com.naiterui.ehp.bs.doctor.prodocl.*;
import com.naiterui.ehp.bs.doctor.remote.IAdRemoter;
import com.naiterui.ehp.bs.doctor.service.ICityService;
import com.naiterui.ehp.bs.doctor.service.IDrPatientRemarkService;
import com.naiterui.ehp.bs.doctor.service.IPatientGroupService;
import com.naiterui.ehp.bs.doctor.service.IPatientService;
import com.naiterui.ehp.bs.doctor.utils.ABCUtil;
import com.naiterui.ehp.bs.doctor.utils.LimitValueConfig;
import com.naiterui.ehp.bs.doctor.utils.PatientUtil;
import com.naiterui.ehp.bs.doctor.utils.PropertyValueConstants;
import com.naiterui.ehp.bs.doctor.vo.*;
import com.naiterui.ehp.bs.doctor.vo.casefile.CaseDetailVO;
import com.naiterui.ehp.bs.doctor.vo.casefile.CaseImgVO;
import com.naiterui.ehp.bs.doctor.vo.casefile.CaseListVO;
import com.naiterui.ehp.bs.doctor.vo.patient.FamilyLiteVO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollectionUtil;
import lombok.SneakyThrows;

@Service
public class AdPatientServiceImpl implements IPatientService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AdPatientServiceImpl.class);
    // v2.8.0发布时间
    private static final String PUBLISH_TIME = ConfigUtil.getString("publishTime.2.8.0");
    /**
     * 病例档案列表url：<code>PATIENT_CASE_LIST_URL</code>
     */
    private static final String PATIENT_CASE_LIST_URL = ConfigUtil.getString("ap.case.list");
    /**
     * 病例详情接口url：<code>PATIENT_CASE_DETAIL_URL</code>
     */
    private static final String PATIENT_CASE_DETAIL_URL = ConfigUtil.getString("ap.case.detail");
    @Autowired
    private IDoctorDao doctorDao;
    @Autowired
    private IPatientDao patientDao;
    @Autowired
    private ICityService cityService;
    @Autowired
    private IDrSpecPatientDao drSpecPatientDao;
    @Autowired
    private IPatientPersonDoctorDao patientPersonDoctorDao;
    @Autowired
    private IDrMessageConsultChargeDao drMessageConsultChargeDao;
    @Autowired
    private IDrPatientGroupDetailDao patientGroupDetailDao;
    @Autowired
    private IAdRemoter remoter;
    /**
     * IM相关设置信息持久化对象
     */
    @Autowired
    private AdImSetupDao adImSetupDao;
    @Autowired
    private IDrPatientRemarkService drPatientRemarkNameService;
    @Autowired
    private IPatientGroupService patientGroupService;
    // 患者档案转mongo持久层
    @Autowired
    private AdPatientDocumentDao mAdPatientDocumentDao;
    @Autowired
    private IDrPatientGroupDetailDao drPatientGroupDetailDao;
    @Autowired
    private IPatientpersonalDoctorLogDao patientPersonDoctorLogDao;
    @Autowired
    private EmrFeignClient emrFeignClient;
    @Autowired
    private PatientFeignClient patientFeignClient;

    /**
     * 拼接前端需要的患者详情数据
     *
     * @param patientId
     * @param doctorId
     *
     * @return
     */
    @Override
    public PatientDetailVo getPatientDetail(Long doctorId, Long patientId) throws BusinessException {
        PatientDetailVo detailVO = new PatientDetailVo();
        // 获取患者基础信息
        Patient patient = this.patientDao.get(patientId);
        if (patient == null) {
            LOGGER.error("查询医生 " + doctorId + " 患者 " + patientId + " 的基本详情信息失败");
            throw new BusinessException(ExceptionCodes.PATIENT_RECORD_NOT_EXSIT);
        }
        // 处理基本信息，优先取医生备注信息
        this.handlePatientBaseInfo(doctorId, detailVO, patient);

        if (patient.getAccountStatus() == PropertyValueConstants.PATIENT_ACCOUNT_STATUS_DISABLED) {
            detailVO.setAccountStatus(PropertyValueConstants.PATIENT_ACCOUNT_STATUS_DISABLED);
        } else {
            detailVO.setAccountStatus(PropertyValueConstants.PATIENT_ACCOUNT_STATUS_OK);
        }

        // ---其他详情

        // 获取图文咨询收费金额
        Integer consultCost = this.drMessageConsultChargeDao.getChargeById(doctorId, patientId);
        // 会话收费金额
        detailVO.setConsultCost(consultCost);

        // 获取患者档案信息
        MPatientDocument patientDocument = this.mAdPatientDocumentDao.queryDocument(patientId);
        // 处理病情描述信息，供显示
        PatientDetailVo diseaseDesc = this.handleDiseaseDesc(patientDocument, true);
        detailVO.setMaritalStatus(diseaseDesc.getMaritalStatus());// 婚姻状况
        detailVO.setHeight(diseaseDesc.getHeight());
        detailVO.setWeight(diseaseDesc.getWeight());
        detailVO.setMedicationAllergy(diseaseDesc.getMedicationAllergy());// 过敏药物历史 --
        detailVO.setPastDisease(diseaseDesc.getPastDisease());// -既往病史 --
        detailVO.setFamilyHistory(diseaseDesc.getFamilyHistory());// -家庭病史 --
        detailVO.setHereditaryDisease(diseaseDesc.getHereditaryDisease());// -遗传病史 --
        detailVO.setSmokeHistory(diseaseDesc.getSmokeHistory());// -吸烟情况
        detailVO.setDrinkHstory(diseaseDesc.getDrinkHstory());// -饮酒情况 --

        return detailVO;
    }


    /**
     * PatientServiceImpl.findPatientABCList() Modified By Liudelong @ 2015/12/3
     * 添加版本号参数,添加备注名显示
     *
     * @param doctorId
     * @param groupId
     *
     * @return
     *
     * @throws BusinessException
     */
    @Override
    public List<ABCVO<PatientVo>> findPatientABCList(Long doctorId, Long groupId) {

        // 根据医生患者关系表，查询医生所有的患者id
        List<Long> patientPsnDrList = this.patientDao.findPatientListByDoctorId(doctorId);

        if (groupId != null) {
            // 排除掉分组中的患者
            List<Long> groupIds = new ArrayList<>();
            groupIds.add(groupId);
            // 处方单分组中已经存在的患者id
            List<Long> patientIdList = this.drPatientGroupDetailDao.getPatientIdsByGroupIds(groupIds);
            patientPsnDrList.removeAll(patientIdList);
        }
        // 拼装vo。优先取备注信息from v2.8.0
        List<PatientVo> voList = this.getPatientsByDoctorId(doctorId, patientPsnDrList);

        // 获取指定医生屏蔽的患者列表然后初始化shildPatientMap
        List<Long> shieldPatientIds = this.adImSetupDao.getDrShieldPatientIds(doctorId);
        // 处理用户屏蔽信息 modify by Amber at 20160615 抽离屏蔽患者处理逻辑
        this.handleShield(voList, shieldPatientIds);

        // 获取患者收费信息key：患者id value：收费金额
        Map<Long, Integer> consultCostMap = this.drMessageConsultChargeDao.getChargeListByDoctorId(doctorId, patientPsnDrList);
        voList.forEach(patient -> {
            Integer consultCost = consultCostMap.get(patient.getId()) != null ? consultCostMap.get(patient.getId()) : 0;
            // 图文咨询金额
            patient.setConsultCost(consultCost);
        });
        List<ABCVO<PatientVo>> abcList = new ArrayList<>();
        try {
            abcList = ABCUtil.toABC(voList, "abcName");
        } catch (Exception e) {
            LOGGER.error("获取ABC患者列表失败", e);
        }
        return abcList;
    }

    @Override
    public PageVO<CaseListVO> getCaseList(Long doctorId, Long patientId, int page, int num) throws BusinessException {
        // 远程调用
        return RestHttpUtil.get()
                           .url(String.format(PATIENT_CASE_LIST_URL, doctorId, patientId, page, num))
                           .executeData(new ParameterizedTypeReference<ResponseVO<PageVO<CaseListVO>>>() {
                           });
    }

    @Override
    public CaseDetailVO getCaseDetail(Long doctorId, Long patientId, String caseId) throws BusinessException {

        CaseDetailVO caseDetailVO = RestHttpUtil.get()
                                                .url(String.format(PATIENT_CASE_DETAIL_URL, doctorId, patientId, caseId))
                                                .executeData(new ParameterizedTypeReference<ResponseVO<CaseDetailVO>>() {
                                                });
        // 获取检验单列表
        List<CaseImgVO> checkList = caseDetailVO.getCheckList();
        // 如果检验单大于4张，截取前四张
        if (checkList.size() > 4) {
            checkList = checkList.subList(0, 4);
            caseDetailVO.setCheckList(checkList);
        }
        return caseDetailVO;
    }


    @Override
    public Map<Long, Patient> getPatientMap(Set<Long> patientIds) {

        return this.patientDao.getPatientMap(patientIds);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveSpecPatient(Long doctorId, Long patientId) {
        DrSpecPatient specPatient = this.drSpecPatientDao.getSpecPatient(doctorId, patientId);
        if (specPatient == null) {// 普通患者--》特别关注
            specPatient = new DrSpecPatient(doctorId, patientId);
            this.drSpecPatientDao.saveOrUpdate(specPatient);
        } else {// 特别关注--》普通关注
            this.drSpecPatientDao.deleteSpecPatient(specPatient);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MyPatientVO getMyPatients(Long doctorId) throws Exception {

        // 根据医生患者关系表，查询医生所有的患者id
        List<Long> normalPatientIds = this.patientDao.findPatientListByDoctorId(doctorId);
        // 特别关注患者列表
        List<Long> specPatientIds = this.drSpecPatientDao.getSpecPatientIdsByDoctorId(doctorId);
        //获取患者信息
        List<PatientVo> patientVos = this.getPatientsByDoctorId(doctorId, normalPatientIds);

        //删除特别关心
        normalPatientIds.removeAll(specPatientIds);

        // 获取指定医生屏蔽的患者列表然后初始化shildPatientMap
        List<Long> shieldPatientIds = this.adImSetupDao.getDrShieldPatientIds(doctorId);

        // 获取患者收费信息key：患者id value：收费金额
        Map<Long, Integer> consultCostMap = this.drMessageConsultChargeDao.getChargeListByDoctorId(doctorId, normalPatientIds);

        // 结果集
        MyPatientVO patientVO = new MyPatientVO();

        // 处理特别关注
        if (CollectionUtil.isNotEmpty(specPatientIds)) {
            // 特别关注列表
            // 拼装vo，优先取备注信息 from v2.8.0
            List<PatientVo> specPatients = patientVos.parallelStream()
                                                     .filter(patientVo -> specPatientIds.contains(patientVo.getId()))
                                                     .peek(patient -> {
                                                         // 图文咨询金额
                                                         patient.setConsultCost(Optional.ofNullable(consultCostMap.get(patient.getId())).orElse(0));
                                                     })
                                                     .collect(Collectors.toList());
            // 处理特别关注用户屏蔽信息 modify by Amber at 20160615 抽离屏蔽患者处理逻辑
            this.handleShield(specPatients, shieldPatientIds);

            patientVO.setSpecList(specPatients);
        }

        // 根据患者id,查询患者信息
        // 拼装vo，优先取备注信息 from v2.8.0
        List<PatientVo> normalPatients = patientVos.parallelStream()
                                                   .filter(patientVo -> normalPatientIds.contains(patientVo.getId()))
                                                   .peek(patient -> {
                                                       // 图文咨询金额
                                                       patient.setConsultCost(Optional.ofNullable(consultCostMap.get(patient.getId())).orElse(0));
                                                   })
                                                   .collect(Collectors.toList());
        // 处理用户屏蔽信息等 modify by Amber at 20160615 抽离屏蔽患者处理逻辑
        this.handleShield(normalPatients, shieldPatientIds);

        // ABC 患者列表
        List<ABCVO<PatientVo>> abcPatients;
        try {
            abcPatients = ABCUtil.toABC(normalPatients, "abcName");
        } catch (Exception e) {
            LOGGER.error("我的患者，ABC患者列表异常", e);
            throw new BusinessException(BaseExceptionCodes.FAILED, e);
        }
        patientVO.setAbcList(abcPatients);
        return patientVO;
    }

    @Override
    public PageVO<PatientRemarkInfoVO> getPagedPatientByPeriods(Long doctorId, PageParamVO pageParamVO) {
        // 配置文件中的天数
        String periods = ConfigUtil.getString("patient.new.days");

        // 包含当天，所以往前推n-1天
        int days = Integer.parseInt(periods);
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, -1 * (days - 1));
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        // 往前推30天
        Date startTime = calendar.getTime();
        // V2.8.0版本发布时间
        Date publishTime = DateUtil.parseStrToDateTime(PUBLISH_TIME);
        // 取30天前和V2.8.0发布时间最近的时间
        int compare = startTime.compareTo(publishTime);
        if (compare < 0) {
            LOGGER.debug("发布V2.8.0的时间更近");
            startTime = publishTime;
        }

        // 与医生绑定的患者，按时间段查，并分页
        Page<PatientPersonDoctor> patientPersonDoctor = this.patientPersonDoctorDao.getByPeriods(doctorId, startTime, pageParamVO);

        // 处理返回vo
        PageVO<PatientRemarkInfoVO> pagedPatientVO = this.getPatientInfoByIdList(doctorId, patientPersonDoctor);

        return pagedPatientVO;
    }

    @Override
    public PatientRemarkInfoVO getPatientInfo(Long doctorId, Long patientId) throws BusinessException {
        return this.getPatientInfo(doctorId, patientId, true);
    }

    @Override
    public PatientRemarkInfoVO getPatientInfo(Long doctorId, Long patientId, boolean containsNickname) throws BusinessException {
        // 判断是否存在医患关系
        PatientPersonDoctorPK patientPersonDoctorPK = new PatientPersonDoctorPK();
        patientPersonDoctorPK.setPatientId(patientId);
        patientPersonDoctorPK.setDoctorId(doctorId);
        PatientPersonDoctor patientPersonDoctor = this.patientPersonDoctorDao.get(patientPersonDoctorPK);
        if (patientPersonDoctor == null) {
            LOGGER.info("患者病历页基本信息，不存在该医患关系！doctorId={}, patientId={}", doctorId, patientId);
            throw new BusinessException(ExceptionCodes.DOCTOR_PATIENT_HASHNO_RELATION);
        }

        // 患者基本信息
        Patient patient = this.patientDao.get(patientId);
        if (patient == null) {
            LOGGER.info("患者信息为空！patientId={}", patientId);
            throw new BusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
        }

        // 医生备注的信息
        DrPatientRemarkname remarkInfo = this.drPatientRemarkNameService.getRemarkInfo(doctorId, patientId);
        // bean转vo
        PatientRemarkInfoVO remarkInfoVO = this.markInfoBean2VO(remarkInfo, patient, containsNickname);
        // 需要提示患者填写信息。
        if (PatientRemarkInfoVO.SHOW_TIPS_TRUE.equals(remarkInfoVO.getShowTips())) {
            // 如果备注名为空，则将患者填写姓名赋值到备注名
            //            if (StringUtils.isBlank(remarkInfoVO.getRemarkName())) {
            //                remarkInfoVO.setRemarkName(patient.getName());
            //            }
            // 提示信息
            PatientRemarkInfoVO tipsInfo = new PatientRemarkInfoVO();
            tipsInfo.setRemarkName(null != remarkInfo && StringUtils.isNotBlank(remarkInfo.getRemarkName()) ? remarkInfo.getRemarkName() : "");
            tipsInfo.setNickName(patient.getNickName());
            tipsInfo.setName(PatientUtil.getPatientName(patient, remarkInfo));
            tipsInfo.setGender(remarkInfoVO.getGender());
            tipsInfo.setAge(remarkInfoVO.getAge());
            tipsInfo.setAddress(remarkInfoVO.getAddress());
            remarkInfoVO.setTipsInfo(tipsInfo);
        }

        // 获取图文咨询收费金额
        Integer consultCost = this.drMessageConsultChargeDao.getChargeById(doctorId, patientId);
        // 会话收费金额
        remarkInfoVO.setConsultCost(consultCost);
        // 设置账户状态
        if (patient.getAccountStatus() == PropertyValueConstants.PATIENT_ACCOUNT_STATUS_DISABLED) {
            remarkInfoVO.setAccountStatus(PropertyValueConstants.PATIENT_ACCOUNT_STATUS_DISABLED);
        } else {
            remarkInfoVO.setAccountStatus(PropertyValueConstants.PATIENT_ACCOUNT_STATUS_OK);
        }
        return remarkInfoVO;
    }

    /**
     * 根据患者填写的病情，分字段处理描述信息
     *
     * @param patientDocument 患者病情
     * @param showDefault     是否显示默认的描述“无”
     *
     * @return
     *
     * @Description
     * @History 2017年6月12日 by wangchuan
     */
    @Override
    public PatientDetailVo handleDiseaseDesc(MPatientDocument patientDocument, boolean showDefault) {
        PatientDetailVo detailVO = new PatientDetailVo();
        if (showDefault) {
            detailVO.setMedicationAllergy("无");// 过敏药物历史 --
            detailVO.setPastDisease("无");// -既往病史 --
            detailVO.setFamilyHistory("无");// -家庭病史 --
            detailVO.setHereditaryDisease("无");// -遗传病史 --
            detailVO.setSmokeHistory("无");// -吸烟情况
            detailVO.setDrinkHstory("无");// -饮酒情况 --
        }

        if (patientDocument == null) {
            return detailVO;
        }
        // ---下面每一个详情可能或多或少有遍历和取值拼接变化
        // 患者婚姻状态(0:未婚，1:已婚)
        String maritalStatus = "";
        if (patientDocument.getMaritalStatus() == null || patientDocument.getMaritalStatus() == 0) {// 为空时，默认未婚
            maritalStatus = "未婚";
        } else if (patientDocument.getMaritalStatus() == 1) {
            maritalStatus = "已婚";
        }
        detailVO.setMaritalStatus(maritalStatus);

        // 身高
        String height = "";
        if (patientDocument.getHeight() != null && patientDocument.getHeight() > 0) {
            height = patientDocument.getHeight() + "";
        }
        detailVO.setHeight(height);
        // 体重
        String weight = "";
        if (patientDocument.getWeight() != null && patientDocument.getWeight() > 0) {
            weight = patientDocument.getWeight() + "";
        }
        detailVO.setWeight(weight);
        String lineWord = "\n";// 前端换行符
        // 过敏药物历史 --
        if (patientDocument.getMedicAllergys() != null && patientDocument.getMedicAllergys().getStatus() == 1) {// 过敏记录不为null，且状态为有
            detailVO.setMedicationAllergy(patientDocument.getMedicAllergys().getDefaultNames());
        }
        // -既往病史
        if (patientDocument.getPastDiseases() != null && patientDocument.getPastDiseases().getStatus() == 1) {// 既往病史记录不为null，且状态为有
            detailVO.setPastDisease(patientDocument.getPastDiseases().getDefaultNames());
        }
        // 家庭病史 额外判断 member 成员。 0：父亲；1：母亲；2：兄弟姐妹；3：子女
        if (patientDocument.getFamilyDiseases() != null) {// 家庭病史不为null，且数组非空
            String names = "";
            for (MFamilyDisease disease : patientDocument.getFamilyDiseases()) {
                if (disease.getDisease() != null && StringUtils.isNotEmpty(disease.getDisease().getDefaultNames())) {
                    int member = disease.getMember();
                    if (StringUtils.isNotEmpty(names)) {
                        names += lineWord;
                    }
                    if (member == 0) {
                        names += "父亲：";
                    } else if (member == 1) {
                        names += "母亲：";
                    } else if (member == 2) {
                        names += "兄弟姐妹：";
                    } else if (member == 3) {
                        names += "子女：";
                    }
                    names += disease.getDisease().getDefaultNames();
                }
            }
            if (StringUtils.isEmpty(names) && showDefault) {
                names = "无";
            }
            detailVO.setFamilyHistory(names);
        }

        // -遗传病史
        if (patientDocument.getHereditaryDiseases() != null && patientDocument.getHereditaryDiseases().getStatus() == 1) {// 遗传病记录不为null，且状态为有
            detailVO.setHereditaryDisease(patientDocument.getHereditaryDiseases().getDefaultNames());
        }

        // 吸烟情况
        if (patientDocument.getSmoke() != null && StringUtils.isNotEmpty(patientDocument.getSmoke().getStatus())) {// 吸烟档案记录存在，且状态非空
            MSmoke smoke = patientDocument.getSmoke();
            // 兼容错误数据，2.5.0之前从不吸烟状态前端传值不符，展示做兼容处理。 by zhoupeipei @ 2016-07-11
            if ("2".equals(smoke.getStatus()) || "".equals(smoke.getStatus())) {
                detailVO.setSmokeHistory("从不");
            } else {
                String name = "";
                if ("0".equals(smoke.getStatus())) {
                    name += "吸烟状态：已戒烟";
                } else if ("1".equals(smoke.getStatus())) {
                    name += "吸烟状态：吸烟";
                    if (StringUtils.isNotEmpty(smoke.getSmokeStart()) && Integer.parseInt(smoke.getSmokeStart()) > 0) {
                        name += lineWord + "开始吸烟年龄：" + smoke.getSmokeStart() + "岁";
                    }
                    if (StringUtils.isNotEmpty(smoke.getPerdayNum()) && Integer.parseInt(smoke.getPerdayNum()) > 0) {
                        name += lineWord + "日吸烟量：" + "平均" + smoke.getPerdayNum() + "支";
                    }
                }
                detailVO.setSmokeHistory(name);
            }
        }

        // 饮酒情况
        this.handleDrink(detailVO, patientDocument.getDrink(), lineWord);
        return detailVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrUpdatePatientInfo(Long doctorId, Long patientId, PatientRemarkInfoVO patientInfoVO) throws BusinessException {
        // 校验参数
        this.validateRemarkInfo(patientInfoVO);

        Doctor doctor = this.doctorDao.get(doctorId);
        if (doctor == null) {
            LOGGER.info("医生信息为空！doctorId={}", doctorId);
            throw new BusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }
        // 患者基本信息
        Patient patient = this.patientDao.get(patientId);
        if (patient == null) {
            LOGGER.info("患者信息为空！patientId={}", patientId);
            throw new BusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
        }

        // 判断是否存在医患关系
        PatientPersonDoctorPK patientPersonDoctorPK = new PatientPersonDoctorPK();
        patientPersonDoctorPK.setPatientId(patientId);
        patientPersonDoctorPK.setDoctorId(doctorId);
        PatientPersonDoctor patientPersonDoctor = this.patientPersonDoctorDao.get(patientPersonDoctorPK);
        if (patientPersonDoctor == null) {
            LOGGER.info("医生修改患者备注信息，不存在该医患关系！doctorId={}, patientId={}", doctorId, patientId);
            throw new BusinessException(ExceptionCodes.DOCTOR_PATIENT_HASHNO_RELATION);
        }

        DrPatientRemarkname remarkInfo = this.drPatientRemarkNameService.getRemarkInfo(doctorId, patientId);
        if (remarkInfo == null) {
            remarkInfo = new DrPatientRemarkname(doctorId, patientId);
            remarkInfo.setCreatedAt(new Date());
            remarkInfo.setCreatedBy("ramarkinfo");
        }
        BeanUtils.copyProperties(patientInfoVO, remarkInfo);
        remarkInfo.setChangedAt(new Date());
        // 有备注信息历史数据，需要更新该字段
        remarkInfo.setChangedBy("ramarkinfo");
        // 置为“已编辑”，不再提示患者填写信息
        remarkInfo.setEdited(DrPatientRemarkname.EDITED_TRUE);
        this.drPatientRemarkNameService.saveOrUpdateRemarkInfo(remarkInfo);
    }

    /**
     * 校验患者备注信息
     *
     * @param patientInfoVO
     *
     * @throws BusinessException
     * @Description
     * @History 2017年6月13日 by wangchuan
     */
    private void validateRemarkInfo(PatientRemarkInfoVO patientInfoVO) throws BusinessException {
        // 姓名（原备注名）
        String remarkName = patientInfoVO.getRemarkName();
        // 填写了姓名
        if (StringUtils.isNotEmpty(remarkName)) {
            if (!LimitValueConfig.validateLimit(LimitValueConfig.LIMIT_PATIENTREMARKNAME, remarkName)) {
                LOGGER.info("医生修改患者备注信息，备注名长度超限");
                throw new BusinessException(ExceptionCodes.INVALID_CONTENT_LENGTH);
            }
            // 表情字符拦截
            if (!remarkName.trim().matches(NameUtils.VALIDATE_REGEX_NAME)) {
                LOGGER.info("医生修改患者备注信息，备注名中包含有特殊字符！！remarkName:{}", remarkName);
                throw new BusinessException(ExceptionCodes.EMOJI_ERROR);
            }
        }
        Integer age = patientInfoVO.getAge();
        // 填写了年龄
        if (age != null) {
            int ageInt = age;
            // 1-200岁
            if (ageInt < 1 || ageInt > 200) {
                LOGGER.info("医生修改患者备注信息，年龄范围不合理！ age:{}", age);
                throw new BusinessException(ExceptionCodes.REMARK_AGE_INPUT_INVALID);
            }
        }

        // 地址
        String address = patientInfoVO.getAddress();
        // 0-50字符
        if (!LimitValueConfig.validateLimit(LimitValueConfig.LIMIT_PATIENT_ADDRESS, address)) {
            LOGGER.info("医生修改患者备注信息，地址长度超限");
            throw new BusinessException(ExceptionCodes.INVALID_CONTENT_LENGTH);
        }
        // 校验表情符号
        if (StringUtils.isNotEmpty(address) && !address.matches(NameUtils.VALIDATE_REGEX_OTHERS)) {
            LOGGER.info("医生修改患者备注信息，地址包含表情符号");
            throw new BusinessException(ExceptionCodes.EMOJI_ERROR);
        }

    }

    /**
     * 备注信息vo的拼装。
     *
     * @param remarkInfo       备注信息
     * @param patient          患者信息
     * @param containsNickname 是否取患者昵称。true取，false不取。默认获取昵称
     *
     * @return
     *
     * @Description 优先备注信息，没有则取患者填写信息
     * @History 2017年6月14日 by wangchuan
     */
    private PatientRemarkInfoVO markInfoBean2VO(DrPatientRemarkname remarkInfo, Patient patient, boolean... containsNickname) {
        PatientRemarkInfoVO remarkInfoVO = new PatientRemarkInfoVO();
        Long patientId = patient.getId();
        remarkInfoVO.setPatientId(patientId);
        remarkInfoVO.setPatientIcon(patient.getHeadUrl());
        remarkInfoVO.setRemarkName(remarkInfo != null ? remarkInfo.getRemarkName() : "");

        // 默认需要取昵称
        boolean fetchNickname = true;
        if (containsNickname.length > 0) {
            fetchNickname = containsNickname[0];
        }
        // 优先取患者名字，再才取昵称
        remarkInfoVO.setName(fetchNickname ? PatientUtil.getPatientName(patient, null) : patient.getName());
        remarkInfoVO.setNickName(patient.getNickName());
        // 有备注信息，且已经编辑保存过：全部返回备注信息
        if (remarkInfo != null && DrPatientRemarkname.EDITED_TRUE.equals(remarkInfo.getEdited())) {
            // 不提示患者填写的信息
            remarkInfoVO.setShowTips(PatientRemarkInfoVO.SHOW_TIPS_FALSE);
            remarkInfoVO.setGender(remarkInfo.getGender());
            remarkInfoVO.setAge(remarkInfo.getAge());
            //åremarkInfoVO.setPhone(remarkInfo.getPhone());
            remarkInfoVO.setAddress(remarkInfo.getAddress());
        }
        // 除了备注名，其他信息都返回患者填写
        else {
            // 提示患者填写的信息
            remarkInfoVO.setShowTips(PatientRemarkInfoVO.SHOW_TIPS_TRUE);
            remarkInfoVO.setGender(patient.getGender());
            remarkInfoVO.setAge(patient.getBirthday() == null ? 0 : patient.getAge());
            //remarkInfoVO.setPhone(patient.getPhone());
            Integer cityId = patient.getCityId();
            String cityName = cityId == null ? "" : this.cityService.getProAndCity(cityId, " ");
            remarkInfoVO.setAddress(cityName);
        }
        remarkInfoVO.setAge((null != remarkInfoVO.getAge() && 0 == remarkInfoVO.getAge()) ? null : remarkInfoVO.getAge());
        return remarkInfoVO;
    }

    @Override
    public List<PatientVo> getPatientsByDoctorId(Long doctorId, List<Long> patientIds) {
        // 医生备注信息map
        Map<Long, DrPatientRemarkname> remarkInfoMap = this.drPatientRemarkNameService.getRemarkInfo2Map(doctorId);
        List<Patient> patientList = this.patientDao.findAllPatientByIds(patientIds);
        if (CollectionUtil.isEmpty(patientList)) {
            return new ArrayList<>();
        }
        // 2018/7/31 上午10:29 王泽浩 这里调用积分服务获取订单
        Map<String, String> paramMap = Maps.newHashMap();
        paramMap.put("doctorId", doctorId + "");
        paramMap.put("patientIds", Joiner.on(",").join(patientIds));
        Map<Long, List<String>> groupNams = this.patientGroupService.getGroupNameByDoctorIdAndPatientIds(doctorId, patientIds);

        // 优先取备注信息的逻辑
        List<PatientVo> voList = patientList.parallelStream().map(patient -> {
            Long patientId = patient.getId();
            PatientRemarkInfoVO remarkInfo = this.markInfoBean2VO(remarkInfoMap.get(patientId), patient);
            PatientVo patientVo = new PatientVo();
            patientVo.setId(patientId);
            patientVo.setCityName(remarkInfo.getAddress());
            String remarkName = remarkInfo.getRemarkName();
            patientVo.setRemarkName(remarkName);
            // 如果备注名为空则取用户姓名
            String abcName = StringUtils.isEmpty(remarkName) ? remarkInfo.getName() : remarkName;
            patientVo.setAbcName(abcName);
            String abcKey = FirstLetterUtil.getNameFristChar(abcName);
            patientVo.setAbcKey(abcKey);
            patientVo.setName(remarkInfo.getName());
            patientVo.setGender(remarkInfo.getGender());
            Integer age = remarkInfo.getAge();
            patientVo.setAge((null != age && 0 == age) ? null : age);
            patientVo.setPatientIcon(patient.getHeadUrl());
            patientVo.setCreateTime(patient.getCreateAt().getTime());
            Integer cancel = patient.getCancel();
            // 取消关注
            patientVo.setSubscribe(cancel == 2 ? PatientVo.SUBSCRIBE_STATUS_UNSUBSCRIBE : PatientVo.SUBSCRIBE_STATUS_SUBSCRIBE);

            // 患者分组
            patientVo.setMyGroup(groupNams.get(patientId));
            return patientVo;
        }).collect(Collectors.toList());
        return voList;
    }

    /**
     * 处理饮酒情况
     * PatientServiceImpl.handleDrink()
     *
     * @param drink
     * @param lineWord
     *
     * @Author guoyongxiang
     * @Date 2017年6月6日
     * @since 1.0.0
     */
    private void handleDrink(PatientDetailVo detailVO, MDrink drink, String lineWord) {
        if (drink != null && drink.getFrequency() != null) {// 饮酒记录存在，且饮酒频率不为null
            int frequency = drink.getFrequency();
            if (frequency == 0) {
                detailVO.setDrinkHstory("饮酒频率：从不");
            } else {
                String names = "";
                if (frequency == 1) {
                    names += "饮酒频率：偶尔";
                }
                if (frequency == 2) {
                    names += "饮酒频率：经常";
                }
                if (frequency == 3) {
                    names += "饮酒频率：每天";
                }
                if (StringUtils.isNotEmpty(drink.getPerdayNum()) && Integer.parseInt(drink.getPerdayNum()) > 0) {
                    names += lineWord + "日饮酒量：" + drink.getPerdayNum() + "两";
                }
                if (StringUtils.isNotEmpty(drink.getDrinkStart()) && Integer.parseInt(drink.getDrinkStart()) > 0) {
                    names += lineWord + "开始饮酒年龄：" + drink.getDrinkStart() + "岁";
                }
                if (StringUtils.isNotEmpty(drink.getDrinkStatus())) {
                    if ("0".equals(drink.getDrinkStatus())) {
                        names += lineWord + "未戒酒";
                    } else if ("1".equals(drink.getDrinkStatus()) && StringUtils.isNotEmpty(drink.getStopAge())) {
                        names += lineWord + "已戒酒，戒酒年龄：" + drink.getStopAge() + "岁";
                    }
                }

                MSelectOrOthers wines = drink.getWines();
                if (wines != null) {
                    names += lineWord + "饮酒种类：" + wines.getDefaultNames();
                }
                detailVO.setDrinkHstory(names);
            }
        }

    }


    /**
     * 处理患者信息。屏蔽状态等 PatientServiceImpl.handlePatientVO()
     *
     * @param patients
     *
     * @Author Amber
     * @Date 2016年6月18日
     * @since 1.0.0
     */
    private void handleShield(List<PatientVo> patients, List<Long> shieldPatientIds) {
        if (CollectionUtil.isNotEmpty(patients) && CollectionUtil.isNotEmpty(shieldPatientIds)) {
            // 处理患者信息
            // begin根据shildPatientMap，对医生全部的患者进行过滤，标记已经被屏蔽的患者
            patients.stream().filter(vo -> shieldPatientIds.contains(vo.getId())).forEach(vo -> vo.setShield(true));
        }
    }

    /**
     * PatientServiceImpl.buildScanSource()
     *
     * @param sourceList
     * @param resultList
     *
     * @Author mudou
     * @Date 2016年11月24日
     * @since 2.6.5
     */
    private void buildScanSource(List<PatientPersonDoctor> sourceList, List<PatientVo> resultList) {

        if (null == sourceList || sourceList.isEmpty()) {
            return;
        }
        Map<Long, Integer> sourceMap = sourceList.stream()
                                                 .collect(Collectors.toMap(e -> e.getPersonPK()
                                                                                 .getPatientId(), PatientPersonDoctor::getSource, (a, b) -> b));
        // 非互联网医院扫码，即为APP扫码，视频部分异常数据无source
        resultList.forEach(patientVo -> {
            Integer source = sourceMap.get(patientVo.getId());
            if (null != source && source == PatientPersonDoctor.SCAN_SOUREC_PC) {
                patientVo.setSource(PatientVo.SCAN_CODE_SOURCE_PC);
            } else {
                patientVo.setSource(PatientVo.SCAN_CODE_SOURCE_APP);
            }
        });
    }

    /**
     * 根据私人医生列表，拼装分页的患者信息vo
     *
     * @param doctorId            医生id
     * @param patientPersonDoctor 私人医生关系
     *
     * @return
     *
     * @Description
     * @History 2017年6月10日 by wangchuan
     */
    private PageVO<PatientRemarkInfoVO> getPatientInfoByIdList(Long doctorId, Page<PatientPersonDoctor> patientPersonDoctor) {
        List<PatientRemarkInfoVO> patientInfoList = new ArrayList<>();

        // 医生-患者关联集合
        List<PatientPersonDoctor> patientPersonalList = patientPersonDoctor.getResult();
        if (patientPersonalList == null || patientPersonalList.size() <= 0) {
            return new PageVO<>();
        }
        // 患者id集合
        Set<Long> patientIds = patientPersonalList.stream().map(pd -> pd.getPersonPK().getPatientId()).collect(Collectors.toSet());
        if (patientIds.isEmpty()) {
            return new PageVO<>();
        }

        // 医生备注信息map
        Map<Long, DrPatientRemarkname> remarkInfoMap = this.drPatientRemarkNameService.getRemarkInfo2Map(doctorId, new ArrayList<>(patientIds));
        // 患者完全信息map
        Map<Long, Patient> patientMap = this.patientDao.getPatientMap(patientIds);
        for (PatientPersonDoctor pd : patientPersonalList) {
            Long patientId = pd.getPersonPK().getPatientId();
            Patient patient = patientMap.get(patientId);
            if (patient == null) {
                LOGGER.info("分页获取新患者，患者信息为空：{}", patientId);
                continue;
            }
            PatientRemarkInfoVO patientInfo = new PatientRemarkInfoVO();
            patientInfo.setPatientId(patientId);
            patientInfo.setPatientIcon(patient.getHeadUrl());

            DrPatientRemarkname remarkInfo = remarkInfoMap.get(patientId);
            // 备注名
            patientInfo.setRemarkName(remarkInfo != null ? remarkInfo.getRemarkName() : "");
            // 优先取患者名字，再才取昵称
            patientInfo.setName(PatientUtil.getPatientName(patient, null));
            patientInfo.setNickName(patient.getNickName());
            // 有备注信息，且已经编辑保存过：全部返回备注信息
            if (remarkInfo != null && DrPatientRemarkname.EDITED_TRUE.equals(remarkInfo.getEdited())) {
                patientInfo.setGender(remarkInfo.getGender());
                patientInfo.setAge(remarkInfo.getAge());
            } else {
                patientInfo.setGender(patient.getGender() == null ? Patient.GENDER_UNKNOWN : patient.getGender());
                patientInfo.setAge(patient.getAge());
            }

            patientInfo.setAge((null != patientInfo.getAge() && 0 == patientInfo.getAge()) ? null : patientInfo.getAge());

            // 关注医生时间
            patientInfo.setCreateTime(pd.getCreateAt().getTime());
            // 扫码来源。非互联网医院扫码，即为APP扫码，视频部分异常数据无source
            Integer source = pd.getSource();

            if (null != source) {
                if (source == PatientPersonDoctor.SCAN_SOUREC_PC) {
                    patientInfo.setSource(PatientVo.SCAN_CODE_SOURCE_PC);
                } else if (source == PatientPersonDoctor.SCAN_SOUREC_CONSULT_ACTIVITY) {
                    patientInfo.setSource(PatientVo.SCAN_CODE_SOURCE_CONSULT_ACTIVITY);
                } else if (source == PatientPersonDoctor.SCAN_CODE_SOURCE_CONSULT_ROOM) {
                    patientInfo.setSource(PatientVo.SCAN_CODE_SOURCE_CONSULT_ROOM);
                } else {
                    patientInfo.setSource(PatientVo.SCAN_CODE_SOURCE_APP);
                }
            } else {// 当为空的设置app
                patientInfo.setSource(PatientVo.SCAN_CODE_SOURCE_APP);
            }

            // 注册来源、注册显示文案、注册提示文案
            if (null != source) {
                if (source == PatientPersonalTips.SOURCE_CONSULT_ACTIVITY.getSource().intValue()) {
                    patientInfo.setSourceMsg(PatientPersonalTips.SOURCE_CONSULT_ACTIVITY.getSourceMsg());
                    patientInfo.setSourceTips(PatientPersonalTips.SOURCE_CONSULT_ACTIVITY.getSourceTips());
                }
            }
            patientInfoList.add(patientInfo);
        }

        return CustomizedPageConvertor.pageConvert(patientPersonDoctor, patientInfoList);
    }

    /**
     * 处理患者基本信息
     *
     * @param doctorId 医生id
     * @param detailVO 结果vo
     * @param patient  患者基本信息
     *
     * @Description 优先取备注信息
     * @History 2017年6月14日 by wangchuan
     */
    private void handlePatientBaseInfo(Long doctorId, PatientDetailVo detailVO, Patient patient) {
        // 患者id
        Long patientId = patient.getId();
        // 医生备注的信息
        DrPatientRemarkname remarkInfo = this.drPatientRemarkNameService.getRemarkInfo(doctorId, patientId);
        detailVO.setPatientId(patientId);
        // 患者头像
        String headUrl = "";
        if (StringUtils.isNotBlank(patient.getHeadUrl())) {
            headUrl = patient.getHeadUrl();
        }
        detailVO.setHeadUrl(headUrl);
        // 备注名
        detailVO.setRemarkName(remarkInfo != null ? remarkInfo.getRemarkName() : "");
        // 优先取患者名字，再才取昵称
        detailVO.setName(PatientUtil.getPatientName(patient, null));

        // 有备注信息，且已经编辑保存过：全部返回备注信息
        if (remarkInfo != null && DrPatientRemarkname.EDITED_TRUE.equals(remarkInfo.getEdited())) {
            // 不提示患者填写信息
            detailVO.setGender(PatientUtil.getGenderName(remarkInfo.getGender()));
            // 患者年龄
            detailVO.setAge(remarkInfo.getAge());
            // 患者地区
            detailVO.setCity(remarkInfo.getAddress());
            // 患者电话
            String phone = remarkInfo.getPhone();
            if (!StringUtils.isEmpty(phone) && phone.length() == 11) {
                phone = phone.substring(0, 3) + "****" + phone.substring(7, 11);
            }
            detailVO.setPhone(phone);
        }
        // 没有备注信息：除了备注名，其他信息都返回患者填写
        else {
            // 患者性别(0:女，1:男)
            detailVO.setGender(patient.getGenderStr());
            // 患者年龄
            detailVO.setAge(patient.getAge());
            // 患者地区
            String areaName = "";
            try {
                areaName = this.cityService.getFullCityName(patient.getCityId(), "").replace("中国", "");
            } catch (Exception ignored) {
            }
            detailVO.setCity(areaName);
            // 患者电话
            String phone = patient.getPhone();
            if (!StringUtils.isEmpty(phone) && phone.length() == 11) {
                phone = phone.substring(0, 3) + "****" + phone.substring(7, 11);
            }
            detailVO.setPhone(phone);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveRemarkname(Long doctorId, Long patientId, String patientName, Integer gender, Integer age, String ageUnit)
            throws BusinessException {
        Doctor doctor = this.doctorDao.get(doctorId);
        if (doctor == null) {
            LOGGER.info("医生信息为空！doctorId={}", doctorId);
            throw new BusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }
        // 患者基本信息
        Patient patient = this.patientDao.get(patientId);
        if (patient == null) {
            LOGGER.info("患者信息为空！patientId={}", patientId);
            throw new BusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
        }

        // 判断是否存在医患关系
        PatientPersonDoctorPK patientPersonDoctorPK = new PatientPersonDoctorPK();
        patientPersonDoctorPK.setPatientId(patientId);
        patientPersonDoctorPK.setDoctorId(doctorId);
        PatientPersonDoctor patientPersonDoctor = this.patientPersonDoctorDao.get(patientPersonDoctorPK);
        if (patientPersonDoctor == null) {
            LOGGER.info("反写患者姓名，不存在该医患关系！doctorId={}, patientId={}", doctorId, patientId);
            throw new BusinessException(ExceptionCodes.DOCTOR_PATIENT_HASHNO_RELATION);
        }

        // 备注信息
        DrPatientRemarkname remarkInfo = this.drPatientRemarkNameService.getRemarkInfo(doctorId, patientId);
        // 已设置备注名的患者不更新
        if (remarkInfo != null && StringUtils.isNotBlank(remarkInfo.getRemarkName())) {
            LOGGER.info("备注名不为空，不反写姓名");
            return false;
        }
        // 备注信息为空,患者姓名不为空不更新
        if (remarkInfo == null && StringUtils.isNotBlank(patient.getName())) {
            LOGGER.info("患者姓名不为空，不反写姓名");
            return false;
        }
        // 根据姓名、性别、年龄是否是本人
        try {
            Integer flag = this.remoter.judgeOneself(patientId, patientName, gender, age, ageUnit);
            if (flag == 2) {
                LOGGER.info("判断是否本人：不是本人");
                return false;
            }
        } catch (BusinessException e) {
            LOGGER.info("判断是否本人异常,patientId:{},name:{},gender:{},age:{},ageUnut:{}", patientId, patientName, gender, age, ageUnit);
            throw new BusinessException(ExceptionCodes.FAILED);
        }

        // 备注信息为空，添加一条记录
        if (remarkInfo == null) {
            remarkInfo = new DrPatientRemarkname(doctorId, patientId);
            remarkInfo.setCreatedAt(new Date());
            remarkInfo.setCreatedBy("prescription");
            // 医生没有编辑过患者的其他信息
            remarkInfo.setEdited(DrPatientRemarkname.EDITED_FALSE);
        } else {
            remarkInfo.setChangedAt(new Date());
            // 有备注信息历史数据，需要更新该字段
            remarkInfo.setChangedBy("prescription");
        }

        remarkInfo.setRemarkName(patientName);
        this.drPatientRemarkNameService.saveOrUpdateRemarkInfo(remarkInfo);

        return true;
    }

    @Override
    public RecomPatientBO getInnerPatient(Long doctorId, Long patientId, Integer fetchNickname) throws BusinessException {
        // 默认获取昵称
        boolean containsNickname = true;
        if (CommonConstant.FETCH_PT_NICKNAME_FALSE.equals(fetchNickname)) {
            containsNickname = false;
        }
        // 患者基本信息
        Patient patient = this.patientDao.get(patientId);
        if (patient == null) {
            throw new BusinessException(ExceptionCodes.USER_NOT_EXIST);
        }

        RecomPatientBO innerPatientVO = new RecomPatientBO();
        innerPatientVO.setId(patientId);

        innerPatientVO.setUnionId(patient.getUnionId());
        String headerUrl = patient.getHeadUrl();
        if (headerUrl == null || "/0".equals(headerUrl)) {
            headerUrl = "";
        }
        innerPatientVO.setHeadUrl(headerUrl);
        if (doctorId != null && Patient.REAL_STATUS_UNAUDIT == patient.getStatus()) {
            // 医生备注的信息
            DrPatientRemarkname remarkInfo = this.drPatientRemarkNameService.getRemarkInfo(doctorId, patientId);
            // 患者信息(备注信息优先于基本信息)
            PatientRemarkInfoVO remarkInfoVO = this.markInfoBean2VO(remarkInfo, patient, containsNickname);
            //innerPatientVO.setPhone(remarkInfoVO.getPhone());
            // 备注名>姓名>昵称(如果需要获取)
            String remarkName = remarkInfoVO.getRemarkName();
            innerPatientVO.setName(StringUtils.isBlank(remarkName) ? remarkInfoVO.getName() : remarkName);
            innerPatientVO.setAge(remarkInfoVO.getAge());
            innerPatientVO.setGender(remarkInfoVO.getGender());
        } else {
            innerPatientVO.setPhone(patient.getPhone());
            innerPatientVO.setName(containsNickname ? PatientUtil.getPatientName(patient, null) : patient.getName());
            innerPatientVO.setAge(patient.getAge());
            innerPatientVO.setGender(patient.getGender());
        }
        this.getPreciseAge(innerPatientVO, innerPatientVO.getAge(), patient.getBirthday());
        innerPatientVO.setStatus(patient.getStatus());
        innerPatientVO.setIdCard(patient.getIdCard());
        return innerPatientVO;
    }

    /**
     * 计算患者的精确年龄
     * PatientServiceImpl.getPreciseAge()
     *
     * @param innerPatientVO
     * @param age
     * @param birthDay
     *
     * @Date 2018年4月23日
     * @since 2.16
     */
    private void getPreciseAge(RecomPatientBO innerPatientVO, Integer age, Date birthDay) {
        if (age != null && age > 0) {
            innerPatientVO.setPreciseAge(age);
            innerPatientVO.setPreciseAgeUnit(AgeUnitEunm.YEAR.getUnit());
            return;
        }

        if (birthDay != null) {
            Date now = new Date();
            int differDays = DateUtil.getTotalDay(birthDay, now);
            int preciseAge = 0;
            String ageUnit = AgeUnitEunm.YEAR.getUnit();
            if (differDays >= 365) {
                preciseAge = differDays / 365;
            } else if (differDays >= 30 && differDays < 365) {
                double dage = differDays / 30.0;
                preciseAge = (int) Math.rint(dage);
                ageUnit = AgeUnitEunm.MONTH.getUnit();
            } else if (differDays >= 0 && differDays < 30) {
                double dage = differDays / 7.0;
                preciseAge = (int) Math.rint(dage);
                if (preciseAge == 0) {
                    preciseAge = 1;
                }
                ageUnit = AgeUnitEunm.WEEK.getUnit();
            }
            innerPatientVO.setPreciseAge(preciseAge);
            innerPatientVO.setPreciseAgeUnit(ageUnit);
        }
    }

    @Override
    public PatientSimpleVO getPatientSimpleInfo(Long doctorId, Long patientId) throws BusinessException {
        PatientSimpleVO patientSimpleVO = new PatientSimpleVO();
        // 获取患者基础信息
        Patient patient = this.patientDao.get(patientId);
        if (patient == null) {
            LOGGER.info("患者不存在，patientId={}", patientId);
            throw new BusinessException(ExceptionCodes.PATIENT_RECORD_NOT_EXSIT);
        }
        // 医生备注的信息
        DrPatientRemarkname remarkInfo = this.drPatientRemarkNameService.getRemarkInfo(doctorId, patientId);
        patientSimpleVO.setId(patientId);
        // 备注名
        patientSimpleVO.setRemarkName(remarkInfo != null ? remarkInfo.getRemarkName() : "");
        // 优先取患者名字，再才取昵称
        patientSimpleVO.setName(PatientUtil.getPatientName(patient, null));
        return patientSimpleVO;
    }

    @Override
    public Map<Long, PatientNameVO> getPatientNames(Long doctorId, String namekey) {

        Map<Long, PatientNameVO> patientNameMap = this.patientDao.getPatientNameByDoctorId(doctorId, namekey);
        return patientNameMap;
    }


    @Override
    public List<MemberVO> getScreenList(Long doctorId, Long patientId) throws BusinessException {
        List<MemberVO> list = new ArrayList<>();
        try {
            List<MedicalRecordScreenBO> boList = this.remoter.getScreenList(doctorId, patientId);
            if (boList != null && !boList.isEmpty()) {
                boList.forEach(bo -> {
                    MemberVO vo = new MemberVO();
                    vo.setName(bo.getRelationName());
                    vo.setValue(bo.getRelation());
                    list.add(vo);
                });
            }
        } catch (BusinessException e) {
            LOGGER.info("调用病历服务查询病历页筛选条件,doctorId:{},patientId:{}", doctorId, patientId);
            throw new BusinessException(ExceptionCodes.HTTP_ERR);
        }
        return list;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deletePatientRelation(Long doctorId, Long patientId) throws BusinessException {
        if (RedisUtil.keyOps().existsKey(doctorId + "_" + patientId)) {
            throw new BusinessException(ExceptionCodes.CONSULT_SESSION_PROCESSING);
        }
        Patient patient = this.patientDao.get(patientId);
        if (patient == null) {
            throw new BusinessException(ExceptionCodes.PATIENT_RECORD_NOT_EXSIT);
        }
        // 清空患者上级
        if (doctorId.equals(patient.getRecomDoctor())) {
            patient.setRecomDoctor(null);
            this.patientDao.update(patient);
        }

        PatientPersonDoctor patientPersonDoctor = this.getPatientPersonDoctor(doctorId, patientId);
        if (patientPersonDoctor.getRelationFlag() == PatientPersonDoctor.RELATION_FLAG_DELETE) {
            LOGGER.warn("患者已经删除不需要再次删除 doctorId:{} patientId:{}", doctorId, patientId);
            return;
        }
        // 删除医患关系
        patientPersonDoctor.setRelationFlag(PatientPersonDoctor.RELATION_FLAG_DELETE);
        patientPersonDoctor.setOperateDate(new Date());
        this.patientPersonDoctorDao.update(patientPersonDoctor);
        //保存医患关系记录
        this.savePatientPersonDoctorLog(doctorId, patientId, 1);
        // 删除特别关心
        DrSpecPatient specPatient = this.drSpecPatientDao.getSpecPatient(doctorId, patientId);
        if (specPatient != null) {
            this.drSpecPatientDao.deleteSpecPatient(specPatient);
        }
        // 删除分组信息
        this.patientGroupDetailDao.removeDoctorPatien(doctorId, patientId);
        // 删除备注名
        this.drPatientRemarkNameService.deleteDrPatientRemark(doctorId, patientId);
        String doctorPatientRelationRedisKey = DoctorPatientRelation.getRedisKey(doctorId, patientId);
        RedisUtil.valueOps().set(doctorPatientRelationRedisKey, PatientPersonDoctor.RELATION_FLAG_DELETE);
//        try {
//            Map<String, String> paramMap = new HashMap<>(2);
//            paramMap.put("doctorId", doctorId + "");
//            paramMap.put("patientId", patientId + "");
//            paramMap.put("triggerSource", ImConstants.SESSION_CLOSE_TRIGGER_SOURCE_DELETE_PATIENT + "");
//            RestHttpUtil.post().url(ConfigUtil.getString("delete.patient.relation")).paramMap(paramMap).executeData();
//        } catch (Exception e) {
//            LOGGER.error("删除患者掉用关闭会话失败 doctorId:{} patient:{}", doctorId, patient, e);
//        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePatientRecover(Long doctorId, List<Long> patientIds) {

        int result = this.patientPersonDoctorDao.updateRelationFlag(doctorId, patientIds);

        if (patientIds.size() != result) {
            LOGGER.warn("医患关系和执行数量不相等，需要执行的数量:{} 最终执行数量:{}", patientIds.size(), result);
        }
        patientIds.forEach(patientId -> {
            //保存医患关系记录
            this.savePatientPersonDoctorLog(doctorId, patientId, 2);
            String redisKey = DoctorPatientRelation.getRedisKey(doctorId, patientId);
            RedisUtil.keyOps().delete(redisKey);
        });

    }

    @Override
    public PatientRealtionStatusBO isdDleteDoctorPatientRelation(Long doctorId, Long patientId) {
        PatientRealtionStatusBO bo = new PatientRealtionStatusBO();
        PatientPersonDoctor patientPersonDoctor = null;
        try {
            patientPersonDoctor = this.getPatientPersonDoctor(doctorId, patientId);
        } catch (BusinessException e) {
            //关联关系不存在
            if (e.getCode() == ExceptionCodes.DOCTOR_PATIENT_HASHNO_RELATION) {
                bo.setRelationStatus(PatientRealtionStatusBO.RELATION_STATUS_NO);
                return bo;
            }
        }

        if (patientPersonDoctor.getRelationFlag() == PatientPersonDoctor.RELATION_FLAG_DELETE) {
            LOGGER.debug("医患关系已经删除 doctorId:{} patientId:{}", doctorId, patientId);
            bo.setRelationStatus(PatientRealtionStatusBO.RELATION_STATUS_DEL);
            return bo;
        }
        bo.setRelationStatus(PatientRealtionStatusBO.RELATION_STATUS_SUCCESS);
        return bo;
    }

    @Override
    public PatientPersonDoctor getPatientPersonDoctor(Long doctorId, Long patientId) throws BusinessException {
        PatientPersonDoctorPK personPK = new PatientPersonDoctorPK();
        personPK.setDoctorId(doctorId);
        personPK.setPatientId(patientId);
        PatientPersonDoctor patientPersonDoctor = this.patientPersonDoctorDao.get(personPK);
        if (patientPersonDoctor == null) {
            LOGGER.warn("医患关系不存在 doctorId:{} patientId:{}", doctorId, patientId);
            throw new BusinessException(ExceptionCodes.DOCTOR_PATIENT_HASHNO_RELATION);
        }
        return patientPersonDoctor;
    }


    @Override
    public List<DeletePatientVO> getPatientDeleteList(Long doctorId) {
        List<Long> patientIds = this.patientPersonDoctorDao.getPatientIds(doctorId, PatientPersonDoctor.RELATION_FLAG_DELETE);
        List<Patient> patients = this.patientDao.findAllPatientByIds(patientIds);
        return Optional.ofNullable(patients)
                       .orElse(Lists.newArrayList())
                       .stream()
                       .map(patient -> new DeletePatientVO(patient.getId(), patient.getHeadUrl(), PatientUtil.getPatientName(patient, null),
                                                           Optional.ofNullable(patient.getGender()).orElse(Patient.GENDER_UNKNOWN), patient.getAge()))
                       .collect(Collectors.toList());
    }

    @Override
    public List<PatientVo> findPatientByPatientId(Long doctorId, String patientIds) {

        // 根据医生患者关系表，查询医生所有的患者id
        List<Long> patientPsnDrList = this.patientDao.findPatientListByDoctorId(doctorId);
        if (patientPsnDrList == null || patientPsnDrList.size() <= 0) {
            return new ArrayList<>();
        }
        LOGGER.info(">>>>>>>>>>>>findPatientByPatientId  doctorId={}，patientIds={}",doctorId,patientIds);
        List<Long> patientIdList = StringUtil.convertLongStr2List(patientIds, ",");
        // 拼装患者vo，优先取医生（对患者的）备注信息，其次取患者基本信息
        List<PatientVo> resultList = this.getPatientsByDoctorId(doctorId, patientIdList);

        // 患者扫码来源 V2.6.5
        List<PatientPersonDoctor> sourceList = this.patientPersonDoctorDao.getByPatientIds(doctorId, new HashSet<>(patientIdList));
        this.buildScanSource(sourceList, resultList);

        return resultList;
    }

    @Override
    public List<FamilyLiteVO> findFamilyByPatientId(Long patientId) throws BusinessException {
        List<FamilyLiteVO> vos = new ArrayList<>();
        List<Long> familyIds = this.emrFeignClient.findFamilysByPatient(patientId);
        if (CollectionUtil.isNotEmpty(familyIds)) {
            List<FamilyBO> bos = this.patientFeignClient.findFamilys(familyIds);
            if (CollectionUtil.isNotEmpty(bos)) {
                vos = bos.stream().map(s -> {
                    FamilyLiteVO vo = new FamilyLiteVO();
                    BeanUtils.copyProperties(s, vo);
                    vo.setAge(s.getAge() + s.getAgeUnit());
                    return vo;
                }).collect(Collectors.toList());
            }
        }
        return vos;

    }

    /**
     * 根据医生ID保存医患关系当前患者数量
     *
     * @param doctorId 医生ID
     */
    private void savePatientPersonDoctorLog(Long doctorId, Long patientId, Integer operateType) {
        LOGGER.info("根据医生ID保存医患关系当前患者数量 请求参数：doctorId = {}", doctorId);
        Assert.notNull(doctorId, "医生ID不能为null");
        Assert.notNull(patientId, "患者ID不能为null");
        Assert.notNull(operateType, "操作类型不能为null");
        PatientpersonalDoctorLog patientpersonalDoctorLog = new PatientpersonalDoctorLog();
        patientpersonalDoctorLog.setDoctorId(doctorId);
        patientpersonalDoctorLog.setPatientId(patientId);
        patientpersonalDoctorLog.setOperateType(operateType);
        patientpersonalDoctorLog.setOperateDate(new Date());
        patientpersonalDoctorLog.setChangedAt(new Date());
        patientpersonalDoctorLog.setCreatedAt(new Date());
        patientpersonalDoctorLog.setCreatedBy("system");
        patientpersonalDoctorLog.setChangedBy("system");
        this.patientPersonDoctorLogDao.save(patientpersonalDoctorLog);
    }

    @SneakyThrows
    @Override
    public String getHisH5Url(Long inquirerId) throws BusinessException {
        List<Long> inquireIds = Arrays.asList(inquirerId);
        List<PatientInquirerBO> inquirerList = patientFeignClient.patientInquirerList(inquireIds);
        if (CollectionUtil.isNotEmpty(inquirerList)) {
            PatientInquirerBO inquirer = inquirerList.get(0);
            String idCard = inquirer.getIdCard();
            if (StringUtils.isEmpty(idCard)) {
                throw new BusinessException(ExceptionCodes.PATIENT_INQUIRER_IDCARD_EMPTY);
            }
            String url = ConfigUtil.getString("smh.record.page.url");
            String key = DESPlus.getDesContent(ConfigUtil.getString("smh.key"), idCard);
            return String.format(url, URLEncoder.encode(key, "UTF-8"));
        }
        throw new BusinessException(ExceptionCodes.PATIENT_INQUIRER_NOT_EXIST);
    }

}
